﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace Hangfire.HttpJob.CSRedis
{
    /// <summary>
    /// JobHostedService
    /// </summary>
    internal class JobHostedService : BackgroundService
    {
        readonly HangfireRedisConfig _hangfireConfig;
        public JobHostedService(HangfireRedisConfig hangfireConfig)
        {
            _hangfireConfig = hangfireConfig;
        }

        /// <summary>
        /// ExecuteAsync
        /// </summary>
        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            LoadAssemblyHangfireJob();
            LoadHttpJob();
            LoadAgentJob();
            return Task.CompletedTask;
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            foreach (var item in DisableMultipleQueueAttribute.LockDic)
                item.Value.Dispose();
            return base.StopAsync(cancellationToken);
        }

        #region private methods
        /// <summary>
        /// 加载程序集作业
        /// </summary>
        private void LoadAssemblyHangfireJob()
        {
            foreach (var item in HangfireRedisExtension.Dic)
            {
                var context = new JobContext(item.Key.FullName);
                var attr = item.Key.GetCustomAttribute<JobAttribute>();
                if (attr is RecurringJobAttribute cronAttr)
                {
                    var name = string.IsNullOrWhiteSpace(attr.JobName) ? item.Key.FullName : attr.JobName;
                    cronAttr.Queue = string.IsNullOrWhiteSpace(cronAttr.Queue) ? "default" : cronAttr.Queue;
                    RecurringJob.AddOrUpdate<IJob>(name, job => job.ExecuteAsync(context), cronAttr.Cron, TimeZoneInfo.Local, cronAttr.Queue);
                }
                else if (attr is DelayJobAttribute delayAtrr)
                {
                    delayAtrr.Time = delayAtrr.Time < TimeSpan.Zero ? TimeSpan.Zero : delayAtrr.Time;
                    BackgroundJob.Schedule<IJob>(job => job.ExecuteAsync(context), delayAtrr.Time);
                }
                else
                    Hangfire.BackgroundJob.Enqueue<IJob>(job => job.ExecuteAsync(context));
            }

            HangfireRedisExtension.Dic.Clear();
        }

        /// <summary>
        /// 加载http方式作业
        /// </summary>
        private void LoadHttpJob()
        {
            if (_hangfireConfig.HttpJobList?.Any() == true)
            {
                foreach (var item in _hangfireConfig.HttpJobList)
                {
                    if (string.IsNullOrWhiteSpace(item.JobName) || string.IsNullOrWhiteSpace(item.Url))
                        break;
                    item.JobType = JobType.Http;
                    if (item.TaskType == TaskType.Once && item.Second > 0)
                        Hangfire.BackgroundJob.Schedule<HttpJobInvoker>(m => m.ExecuteAsync(item), TimeSpan.FromSeconds(item.Second));
                    else if (item.TaskType == TaskType.Once)
                        Hangfire.BackgroundJob.Enqueue<HttpJobInvoker>(m => m.ExecuteAsync(item));
                    else if (item.TaskType == TaskType.Cycle && !string.IsNullOrWhiteSpace(item.Cron))
                        RecurringJob.AddOrUpdate<HttpJobInvoker>(item.JobName, m => m.ExecuteAsync(item), item.Cron, TimeZoneInfo.Local, item.Queue);
                }
            }
        }

        /// <summary>
        /// 加载远程agent作业
        /// </summary>
        private void LoadAgentJob()
        {
            if (_hangfireConfig.AgentJobList?.Any() == true)
            {
                foreach (var item in _hangfireConfig.AgentJobList)
                {
                    if (string.IsNullOrWhiteSpace(item.JobName) || string.IsNullOrWhiteSpace(item.ServerUrl) 
                        || string.IsNullOrWhiteSpace(item.AgentUrl))
                        break;
                    item.JobType = JobType.Agent;
                    if (item.TaskType == TaskType.Once && item.Second > 0)
                        Hangfire.BackgroundJob.Schedule<AgentJobInvoker>(m => m.ExecuteAsync(item), TimeSpan.FromSeconds(item.Second));
                    else if (item.TaskType == TaskType.Once)
                        Hangfire.BackgroundJob.Enqueue<AgentJobInvoker>(m => m.ExecuteAsync(item));
                    else if (item.TaskType == TaskType.Cycle && !string.IsNullOrWhiteSpace(item.Cron))
                        RecurringJob.AddOrUpdate<AgentJobInvoker>(item.JobName, m => m.ExecuteAsync(item), item.Cron, TimeZoneInfo.Local, item.Queue);
                }
            }
        }
        #endregion
    }
}